<?php

class DBListItem {
	
	protected $pkName;
	protected $entityName;
	
	/**
	 * Data stored from Database
	 *
	 * @var array
	 */
	protected $rowdata;

	/**
	 * Data generated dynamicaly from $rowData
	 * 
	 * @var array
	 */
	protected $customdata;
	
	/**
	 * Stores function pointers to lazy load list if anyone accesses the field
	 * @var unknown_type
	 */
	protected $lazydata;
	
	/**
	 * Reference to the Parent DbTable
	 * 
	 * @var DBTable
	 */
	private $dbTable;
	
	private $SecurityObject;
	
	//Indicates if the Data of the Row was changed and should be saved
	private $isDirty = false;
	
	//Indicates if the Row is new.
	private $isNew = true;
	
	//Parent Control
	protected $parentItem, $parentList;
	
	public function canRead()   { return $this->SecurityObject == null || $this->SecurityObject->canRead(); }
	public function canCreate() { return $this->SecurityObject == null || $this->SecurityObject->canCreate(); }
	public function canUpdate() { return $this->SecurityObject == null || $this->SecurityObject->canUpdate(); }
	public function canDelete() { return $this->SecurityObject == null || $this->SecurityObject->canDelete(); }
	public function canAdmin()  { return $this->SecurityObject == null || $this->SecurityObject->canAdmin(); }
	public function getSecurityObject() { return $this->SecurityObject; }
	
	public function hasField($name) { return array_key_exists($name, $this->rowdata) || $this->isNew; }
	public function getFields() { return array_keys($this->rowdata); }
	public function getValues() { return array_values($this->rowdata); }
	
	public function getIsNew() { return $this->isNew; }
	public function getIsDirty() { return $this->isDirty; }
	
	public function getParentList() { 
		/*
		if(!$this->parentList) {
			
			// Load List of Parent-Type
			$parentClassName = $this->entityName . "List";
			$this->parentList = new $parentClassName();
			$qry = "GetBy" . $this->pkName;
			$this->parentList->$qry($this->rowdata[$this->pkName]);
		}
*/
		return $this->parentList; 
		
	}
	
	public function getParentItem() {
		
		// Return parentItem or lazy-load it if it not has been loaded
		if(!$this->parentItem) {		
			//$this->getParentList();
			//echo "<pre>"; print_r($this); echo "</pre>";
			//$this->parentItem = $this->getParentList()->At(0); 
		}
		return $this->parentItem;
		
	}
	
	
	public function __construct($data=null, DBList $ParentList=null) {
		
		//echo "---------------------------<br>Creating " . get_class($this) . "<br>";

		/* Initialize custom attributes with default values */
		if(!$this->entityName) { 
			$myClassName = get_class($this);
			$this->entityName = substr($myClassName, 0, strpos($myClassName, "Item")); 
		}
		
		if($this->pkName=='') { $this->pkName = "pk" . $this->entityName; }
		
			
		if(is_array($data)) {
			//Fetch the Data
			
			$this->fetchData($data);
		
			if($ParentList!=null) {
				$this->parentList = $ParentList;
				$this->parentItem = $ParentList->getParentItem();
				
				$this->dbTable = $ParentList->getDbTable();
			}
		}
		elseif(is_numeric($data)) {
			
			//Load by Id
			$parentClassName = $this->entityName . "List";

			//if(!$this->dbTable) { $this->dbTable = "tbl" . $entityName; }
			
			if($parentClassName!="DBList") {
				$list = new $parentClassName();
				
				$this->dbTable = $list->getDbTable();
				
				//Load it From DB
				$sqlA = $list->getSqlSelectQuery();
				$sqlA["WHERE"] = $this->pkName . " = '$data' ";
				
				$sql = $list->generateSqlQuery($sqlA);
				
				$res = MySQLConnection::query($sql);
				$data = @mysql_fetch_array($res);
				
				if($data) {
					$this->fetchData($data);
					$this->isNew = false;
				}
				else {
					//Not found, load new
					$this->isNew = true;
				}
				
				
			}
			else {
				throw new Exception("Error loading DBListItem by PrimaryKey, no DataType is specified");
			}
			
		}
		else {
			$parentClassName = $this->entityName . "List";
			
			if($parentClassName!="DBList") {
				$list = new $parentClassName();
				
				$this->dbTable = $list->getDbTable();
			}
			$this->rowdata = array();
			$this->customdata = array();
			
			$this->isNew = true;
		}
		
		$this->loadCustomData();
		
	}
	
	public final function setTable(DBList $t) {
		$this->dbTable = $t->getDbTable();
	}
	
	public final function fetchData($data) {
		
		$this->isNew = false;
		$this->rowdata = $data;
		
		//Load Security Object
		if(isset($this->rowdata["fkSecurityObject"])) {
			
			if(is_numeric($this->rowdata["fkSecurityObject"])) {

				//load
				$so = new SecurityObject();
				$so->loadSecurityObject($this->rowdata["fkSecurityObject"]);
				
				$this->SecurityObject = $so;
				
			}
			else {
				//Load other...
				$this->SecurityObject = $this->loadSecurityObject();
			}
		}
		else {
			$this->SecurityObject = null;
		}
		
				
		//Load Time Information
		if(isset($data["createDate"])) {
			$this->customdata["createDateTS"] = strtotime($data["createDate"]);
			$this->customdata["createDateDist"] = Utils::getTimeDistanceText($this->customdata["createDateTS"]);
		}

		if(isset($data["changeDate"])) {
			$this->customdata["changeDateTS"] = strtotime($data["changeDate"]);
			$this->customdata["changeDateDist"] = Utils::getTimeDistanceText($this->customdata["changeDateTS"]);
		}
	}
	
	public function save() {
		
		if($this->isDirty) {
			$sql = "";
			if(!$this->isNew) {
				$this->rowdata["changeDate"]  = date("Y-m-d H:i:s");
				$this->rowdata["changeUser"] = Runtime::getCurrentUser()->getPkSecurityUser();
			}
			else {
				$this->rowdata["createDate"]  = date("Y-m-d H:i:s");
				$this->rowdata["createUser"] = Runtime::getCurrentUser()->getPkSecurityUser();
			}
			
			if(!$this->isNew) {
			
				//Check wich fields have changed
				$changedFields = array();
				$oldFieldIndex = 0;
				while((list($field, $value) = each($this->rowdata)) == true) {
					if(!is_numeric($field)) {
					
						//Check field for changes
						if($value != $this->rowdata[$oldFieldIndex]) {
							$changedFields[$field] = $value;
						}
						//echo $field . "is equal id $oldFieldIndex";
						
					$oldFieldIndex++;
					}
				}
				//print_r($changedFields);
				
				if(count($changedFields)) {
					
					$myClassName = get_class($this);
					$entityName = substr($myClassName, 0, strpos($myClassName, "Item"));
						
					$sql.= "UPDATE " . $this->dbTable ." SET \n";
					$sqlUpd="";
					while(list($field, $value) = each($changedFields)) {
						$value = str_replace("'", "\'", $value);
						$sqlUpd.=($sqlUpd?", ":"") . $field . " = " . ($value=="NULL"?$value:"'$value'");
					}
					$sql.=$sqlUpd;
					$sql.="WHERE " . $this->pkName ." = '" . $this->__get($this->pkName) . "'";
					
					return MySQLConnection::update($sql);
				}
			}
			else {

				$myClassName = get_class($this);
				$entityName = substr($myClassName, 0, strpos($myClassName, "Item"));

				$insertFields = array();
				while(list($field, $value) = each($this->rowdata)) {
					if(!is_numeric($field)) {
						$insertFields[$field] = $value;
					}
				}
						
				$sql.= "INSERT INTO " . $this->dbTable ."\n";
				$sqlFields=""; $sqlValues="";
				
				while(list($field, $value) = each($insertFields)) {
					$sqlFields.=($sqlFields?", ":"") . $field;
					$sqlValues.=($sqlValues?", ":"") . ($value=="NULL"?"NULL":"'$value'");
				}
				$sql.="( $sqlFields )\n VALUES ( $sqlValues )";
				
				return MySQLConnection::insert($sql);
			}
		}
	}
	
	public function delete() {
		
		$myClassName = get_class($this);
		$entityName = substr($myClassName, 0, strpos($myClassName, "Item"));

		$sql.= "DELETE FROM " . $this->dbTable ." WHERE pk$entityName = '" . $this->__get("pk".$entityName) . "'\n";
		return MySQLConnection::delete($sql);
		
	}
	
	/* Available only if Items are stored in a List */
	public function setIsFirst($v) {
		$this->customdata["isFirst"] = $v;
		$this->customdata["canMoveUp"] = !$v;
	}
	public function setIsLast($v) {
		$this->customdata["isLast"] = $v;
		$this->customdata["canMoveDown"] = !$v;
	}
	
	/**
	 * Is called if no Security Object is found based on the rowdata, override this function to change this behaviour
	 * @return SecurityObject
	 */
	private function loadSecurityObject() {
		return new SecurityObject();
	}
	
	function __get($name) {

		if($this->canRead()) {

			//Check field in dbData
			if(array_key_exists($name, $this->rowdata)) {
				return stripslashes($this->rowdata[$name]);
			}
			//Check custom data
			elseif(is_array($this->customdata) && array_key_exists($name, $this->customdata)) {
				return $this->customdata[$name];
			}
			
			//Check lazy loadData
			elseif(is_array($this->lazydata) && array_key_exists($name, $this->lazydata)) {
				
				if(!isset($this->customdata[$name])) {
					$f = array($this, $this->lazydata[$name]);
					
					if(is_callable($f)) {
						$this->customdata[$name] = call_user_func($f);
					}
					else {
						user_error("Function $f[0]->$f[1] is not callable");
					} 	
					
				}
				
				return $this->customdata[$name];
								
			}
			elseif($this->isNew) {
				return "";
			}
			else {
				$t = debug_backtrace();
				
				
				echo "<b>Notice</b>: Field '$name' not found in ".get_class($this)." accessed in <b>".$t[0]['file']."</b> on line <b>".$t[0]['line']."</b><br>";	
			}
			
		}
		else {
			return "[blocked]";
		}
	}
	
	function __set($name, $value) {
		
		//TODO: Security??
		if(array_key_exists($name, $this->rowdata) || $this->isNew) {
			if(!isset($this->rowdata[$name]) || $this->rowdata[$name]!=$value) {
				//Set new value
				$this->rowdata[$name] = $value;
				$this->isDirty = true;
			}
		}
		elseif(isset($this->customdata[$name])) {
			//Error: Custom Data cannot be changed
		}
		else {
			//echo "column $name no found";
		}
		
	}
	
	
	//CustomData is normale write protected
	function setCustomData($name, $value) {

		$this->customdata[$name] = $value;
		
	}
	
	/* Is Called in Cunstructor after loading Data */
	protected function loadCustomData() {}
	
}

?>